/* Copyright 2023 Hangzhou Yingyi Technology Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <uk/test.h>
#include <uk/intctlr.h>
#include <uk/plat/lcpu.h>


#ifdef CONFIG_LIBUKINTCTLR_GICV3
#include <uk/intctlr/gic-v3.h>

static uint32_t global_current_cpu;

static int spi_handler(void *args __unused)
{
	uint64_t mpidr = SYSREG_READ64(MPIDR_EL1);
	uint64_t aff = ((mpidr & MPIDR_AFF3_MASK) >> 8) |
				(mpidr & MPIDR_AFF2_MASK) |
				(mpidr & MPIDR_AFF1_MASK) |
				(mpidr & MPIDR_AFF0_MASK);

	global_current_cpu = (uint32_t)aff;
	return 0;
}

UK_TESTCASE_DESC(ukintctlr, set_spi_pendings_test_affx_api,
	"Affinity irq40 to the current cpu")
{
	/* 中断号40，亲和至当前cpu */
	uint32_t original_cpu_affinity = uk_intctlr_spi_get_affinity(40);

	uint64_t current_cpu = SYSREG_READ64(MPIDR_EL1);
	uint64_t aff = ((current_cpu & MPIDR_AFF3_MASK) >> 8) |
					(current_cpu & MPIDR_AFF2_MASK) |
					(current_cpu & MPIDR_AFF1_MASK) |
					(current_cpu & MPIDR_AFF0_MASK);

	uk_intctlr_irq_set_affinity(40, (uint32_t)aff);
	uk_intctlr_irq_register(40, spi_handler, NULL);
	isb();

	uk_intctlr_simulate_spi(40);

	UK_TEST_EXPECT_SNUM_EQ((uint32_t)aff, global_current_cpu);

	/* 恢复现场 */
	uk_intctlr_irq_unregister(40, spi_handler);
	uk_intctlr_irq_set_affinity(40, original_cpu_affinity);
}


UK_TESTCASE_DESC(ukintctlr, verify_spi_priority_level_setting,
	"Compare the priority of interrupt 60/70")
{
	uint8_t irq60_original_prio_level =
		uk_intctlr_irq_get_priority(60) >> TRANS_PRIORITY_SHIFT;
	uint8_t irq70_original_prio_level =
		uk_intctlr_irq_get_priority(70) >> TRANS_PRIORITY_SHIFT;


#if defined(CONFIG_PRIORITY_MAX_16)
	uk_intctlr_irq_set_priority(60, 15);
	uk_intctlr_irq_set_priority(70, 14);
	isb();
	UK_TEST_EXPECT_SNUM_GT(uk_intctlr_irq_get_priority(60),
	uk_intctlr_irq_get_priority(70));
#elif defined(CONFIG_PRIORITY_MAX_32)
	uk_intctlr_irq_set_priority(60, 30);
	uk_intctlr_irq_set_priority(70, 31);
	isb();
	UK_TEST_EXPECT_SNUM_GT(uk_intctlr_irq_get_priority(60),
	uk_intctlr_irq_get_priority(70));
#elif defined(CONFIG_PRIORITY_MAX_64)
	uk_intctlr_irq_set_priority(60, 62);
	uk_intctlr_irq_set_priority(70, 63);
	isb();
	UK_TEST_EXPECT_SNUM_GT(uk_intctlr_irq_get_priority(60),
	uk_intctlr_irq_get_priority(70));
#elif defined(CONFIG_PRIORITY_MAX_128)
	uk_intctlr_irq_set_priority(60, 126);
	uk_intctlr_irq_set_priority(70, 127);
	isb();
	UK_TEST_EXPECT_SNUM_GT(uk_intctlr_irq_get_priority(60),
	uk_intctlr_irq_get_priority(70));
#endif

	/*恢复现场*/
	uk_intctlr_irq_set_priority(60, irq60_original_prio_level);
	uk_intctlr_irq_set_priority(70, irq70_original_prio_level);
}

/* 测试sgi生成接口uk_intctlr_sgi_op
 * 场景: 向当前cpu发送/向除自己外的所有cpu发送
 */
UK_TESTCASE_DESC(ukintctlr, send_sgi_to_bsp_or_all,
	"Send a SGI to current cpu/all cpu excluding self")
{
	int r;

	uint64_t current_cpu = SYSREG_READ64(MPIDR_EL1);
	uint64_t aff = ((current_cpu & MPIDR_AFF3_MASK) >> 8) |
					(current_cpu & MPIDR_AFF2_MASK) |
					(current_cpu & MPIDR_AFF1_MASK) |
					(current_cpu & MPIDR_AFF0_MASK);

	/* 将sgi亲和到当前cpu */
	r = uk_intctlr_sgi_op(8, 1, (uint32_t)aff);

	UK_TEST_EXPECT_ZERO(r);

	/* 将sgi亲和到除自己外的所有cpu */
	r = uk_intctlr_sgi_op(8, 0);

	UK_TEST_EXPECT_ZERO(r);
}

/*
 * 测试接口 uk_intctlr_sgi_op
 * 测试场景：
 * 异常场景:
 * 1. CPU列表中的CPU range selector不相同
 * 2. CPU列表中的CPU亲和属性不相同
 * 3. targetlist的CPU超过系统支持的最大ID号
 * 正常场景:
 * 4. CPU列表中的CPU具有相同亲和性(aff3/2/1)和range selector
 */
UK_TESTCASE_DESC(ukintctlr, affx_and_rs_check,
"Send an SGI to the CPU list in normal and error scenarios")
{
	int r;

	/* 检查是否支持发送至idx 16 及以上的cpu */
	r = uk_intctlr_sgi_op(4, 1, 0x15);
	UK_TEST_EXPECT_SNUM_EQ(r, -1);

	/* 不同的range selector */
	r = uk_intctlr_sgi_op(4, 4, 0, 0x100, 0x200, 0x210);
	UK_TEST_EXPECT_SNUM_EQ(r, -1);

	/* 不同的affx(x = 2) */
	r = uk_intctlr_sgi_op(4, 4, 0x100, 0x200, 0x300, 0x010300);
	UK_TEST_EXPECT_SNUM_EQ(r, -1);

	/* affx/rs 相同*/
	r = uk_intctlr_sgi_op(4, 3, 0x101, 0x102, 0x103);
	UK_TEST_EXPECT_ZERO(r);
}

#endif

uk_testsuite_register(ukintctlr, NULL);
